package com.esender.strategy.filter.impl;

import com.esender.common.constant.CacheConstant;
import com.esender.common.constant.SmsConstant;
import com.esender.common.enums.ExceptionEnums;
import com.esender.common.exception.StrategyException;
import com.esender.common.model.StandardSubmit;
import com.esender.strategy.client.BeaconCacheClient;
import com.esender.strategy.filter.StrategyFilter;
import com.esender.strategy.util.ErrorSendMsgUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
 * 一分钟发送验证码短信限流规则
 * 60s只能发送1次
 */
@Service("limitOneMinute")
@Slf4j
public class LimitOneMinuteStrategyFilter implements StrategyFilter {
    private final String UTC = "+8";
    private static final long ONE_MINUTE = 1000 * 60 -1;

    private static final int MAX_LIMIT = 1;
    @Autowired
    private BeaconCacheClient beaconCacheClient;
    @Autowired
    private ErrorSendMsgUtil errorSendMsgUtil;
    @Override
    public void strategy(StandardSubmit standardSubmit) {
        // 判断短信类型不是验证码类的，直接结束方法
        if(standardSubmit.getState() != SmsConstant.CODE_TYPE){
            return;
        }
        // 1、基于standardSubmit获取短信发送得时间
        LocalDateTime sendTime = standardSubmit.getSendTime();
        // 2、基于LocalDateTime获取短信发送得毫秒值
        Long sendTimeMillis = sendTime.toInstant(ZoneOffset.of(UTC)).toEpochMilli();
        // 3、获取客户得客户标识和手机号
        String mobile = standardSubmit.getMobile();
        Long clientId = standardSubmit.getClientId();
        // 4、优先将当前短信发送信息插入到zset结构中
        String key = CacheConstant.LIMIT_MINUTES + clientId + CacheConstant.SEPARATOR + mobile;
        Boolean zadd = beaconCacheClient.zadd(key, sendTimeMillis, sendTimeMillis);
        // 5、如果插入失败，则说明出现了并发插入得情况。由于60s只能发送1次那么直接告辞
        if(!zadd){
            log.info("【策略模块-一分钟限流策略】  插入失败！ 满足一分钟限流规则，无法发送！");
            standardSubmit.setErrorMsg(ExceptionEnums.ONE_MINUTE_LIMIT + ",mobile = " + mobile);
            errorSendMsgUtil.sendWriteLog(standardSubmit);
            errorSendMsgUtil.sendPushReport(standardSubmit);
            throw new StrategyException(ExceptionEnums.ONE_MINUTE_LIMIT);
        }
        // 6、如果插入成功，那么进行时间范围筛选zrangebyscore，查询1分钟内发送得次数，如果大于等于2那么达到限流规则告辞
        long start = sendTimeMillis - ONE_MINUTE;
        int count = beaconCacheClient.zRangeByScore(key, Double.parseDouble(start + ""), Double.parseDouble(sendTimeMillis + ""));
        if (count > MAX_LIMIT){
            log.info("【策略模块-一分钟限流策略】  满足一分钟限流规则，无法发送！");
            // 由于这里是超过发送次数，所以应该将刚刚存进zset结构中得数据删除掉
            beaconCacheClient.zRemove(key, sendTimeMillis);
            standardSubmit.setErrorMsg(ExceptionEnums.ONE_MINUTE_LIMIT + ",mobile = " + mobile);
            errorSendMsgUtil.sendWriteLog(standardSubmit);
            errorSendMsgUtil.sendPushReport(standardSubmit);
            throw new StrategyException(ExceptionEnums.ONE_MINUTE_LIMIT);
        }
        log.info("【策略模块-一分钟限流策略】  一分钟限流规则通过，可以发送！");
    }
}
